home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / editors / ae92.zoo / eaea.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-27  |  12.8 KB  |  711 lines

  1. ----eaea.c----
  2. /*
  3.  *    ae.c        Anthony's Editor  May '92
  4.  *
  5.  *    Public Domain 1991, 1992 by Anthony Howe.  All rights released.
  6.  */
  7.  
  8. #include <ctype.h>
  9. #include <curses.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12.  
  13. #ifndef BUF
  14. #define BUF    32767
  15. #endif /* BUF */
  16.  
  17. #ifndef HUP
  18. #define HUP    "ae.hup"
  19. #endif /* HUP */
  20.  
  21. #define HELP_LINE    7
  22.  
  23. typedef struct keytable_t {
  24.     int key;
  25.     void (*func)();
  26. } keytable_t;
  27.  
  28. int done;
  29. int row, col;
  30. int point, page, epage;
  31. int input;
  32. int helpline;
  33. char buf[BUF];
  34. char *ebuf;
  35. char *gap = buf;
  36. char *egap;
  37. char *filename;
  38. keytable_t *table;
  39.  
  40. /*
  41.  *    The following assertions must be maintained.
  42.  *
  43.  *    o  buf <= gap <= egap <= ebuf
  44.  *        If gap == egap then the buffer is full.
  45.  *
  46.  *    o  cursor = ptr(point) and cursor < gap or egap <= cursor 
  47.  *
  48.  *    o  page <= point < epage
  49.  *
  50.  *    o  0 <= point <= pos(ebuf) <= BUF
  51.  *
  52.  *
  53.  *    Memory representation of the file:
  54.  *
  55.  *        low    buf  -->+----------+
  56.  *                |  front   |
  57.  *                | of file  |
  58.  *            gap  -->+----------+<-- character not in file 
  59.  *                |   hole   |
  60.  *            egap -->+----------+<-- character in file
  61.  *                |   back   |
  62.  *                | of file  |
  63.  *        high    ebuf -->+----------+<-- character not in file 
  64.  *
  65.  *
  66.  *    point & gap
  67.  *
  68.  *    The Point is the current cursor position while the Gap is the 
  69.  *    position where the last edit operation took place. The Gap is 
  70.  *    ment to be the cursor but to avoid shuffling characters while 
  71.  *    the cursor moves it is easier to just move a pointer and when 
  72.  *    something serious has to be done then you move the Gap to the 
  73.  *    Point. 
  74.  *
  75.  *
  76.  *    Use of stdio for portability.
  77.  *
  78.  *    Stdio will handle the necessary conversions of text files to 
  79.  *    and from a machine specific format.  Things like fixed length 
  80.  *    records; CRLF mapping into <newline> (\n) and back again; 
  81.  *    null padding; control-Z end-of-file marks; and other assorted 
  82.  *    bizare issues that appear on many unusual machines.
  83.  *
  84.  *    AE is meant to be simple in both code and usage.  With that
  85.  *    in mind certain assumptions are made.
  86.  *
  87.  *    Reading:  If a file can not be opened, assume that it is a
  88.  *    new file.  If an error occurs, fall back to a safe state and
  89.  *    assume an empty file.  fread() is typed size_t which is an
  90.  *    unsigned number.  Zero (0) would indicate a read error or an
  91.  *    empty file.  A return value less than BUF is alright, since
  92.  *    we asked for the maximum allowed.
  93.  *
  94.  *    Writing:  If the file can not be opened or a write error occurs,
  95.  *    then we scramble and save the user's changes in a file called 
  96.  *    ae.hup.  If ae.hup fails to open or a write error occurs, then 
  97.  *    we assume that shit happens.
  98.  *
  99.  */
  100.  
  101. int adjust();
  102. int nextline();
  103. int pos();
  104. int prevline();
  105. int save();
  106. char *ptr();
  107.  
  108. void backsp();
  109. void bottom();
  110. void delete();
  111. void display();
  112. void down();
  113. void file();
  114. void help();
  115. void insert();
  116. void insert_mode();
  117. void left();
  118. void lnbegin();
  119. void lnend();
  120. void movegap();
  121. void pgdown();
  122. void pgup();
  123. void redraw();
  124. void right();
  125. void quit();
  126. void flip();
  127. void top();
  128. void up();
  129. void wleft();
  130. void wright();
  131.  
  132. #if TERMCAP
  133. /*
  134.  *    Function Key Support for BSD CURSES.
  135.  *
  136.  *    BSD CURSES does not support function keys as nicely as System V 
  137.  *    or XPG CURSES.  So I've provided some functions that can decode
  138.  *    multi-byte function keys that are commonly supported by BSD's
  139.  *    termcap file.
  140.  *
  141.  *    The KEY_xxxx macro constants are not defined in the same manner
  142.  *    as System V or XPG.  Also the key_table[] can be customised to
  143.  *    support additional key sequences that might not be supported by
  144.  *    termcap.  Create a new key code and provide the key sequence 
  145.  *    string; initkey() will not alter the provided sequence provided
  146.  *    it doesn't match a termcap capability name (see KEY_BACKSPACE).
  147.  */
  148.  
  149. #include <sys/types.h>
  150.  
  151. int initkey();
  152. int getkey();
  153.  
  154. #define KEY_DOWN        (-11)
  155. #define KEY_UP          (-12)
  156. #define KEY_LEFT        (-13)
  157. #define KEY_RIGHT       (-14)
  158. #define KEY_BACKSPACE   (-15)
  159. #define KEY_DC        (-16)
  160. #define KEY_F0          (-20)
  161. #define KEY_F(n)        (KEY_F0-(n))
  162.  
  163. typedef struct key_entry_t {
  164.     short code;
  165.     char *entry;
  166. } key_entry_t;
  167.  
  168. char key_buffer[1024];
  169.  
  170. key_entry_t key_table[] = {
  171.     { KEY_DOWN, "kd" },
  172.     { KEY_UP, "ku" },
  173.     { KEY_LEFT, "kl" },
  174.     { KEY_RIGHT, "kr" },
  175.     { KEY_BACKSPACE, "\b" },
  176.     { KEY_DC, "\177" },
  177.     { KEY_F(0), "k0" },
  178.     { KEY_F(1), "k1" },
  179.     { KEY_F(2), "k2" },
  180.     { KEY_F(3), "k3" },
  181.     { KEY_F(4), "k4" },
  182.     { KEY_F(5), "k5" },
  183.     { KEY_F(6), "k6" },
  184.     { KEY_F(7), "k7" },
  185.     { KEY_F(8), "k8" },
  186.     { KEY_F(9), "k9" },
  187.     { 0, NULL }
  188. };
  189.  
  190. int
  191. initkey()
  192. {
  193.     key_entry_t *k;
  194.     char *ptr, *kbuf, *tname;
  195.     static char buffer[1024];
  196.     
  197.     if ((tname = (char*) getenv("TERM")) == NULL 
  198.     || tgetent(buffer, tname) != 1)
  199.         return (0);
  200.     for (kbuf = key_buffer, k = key_table; k->entry != NULL; ++k) {
  201.         ptr = (char*) tgetstr(k->entry, &kbuf);
  202.         if (ptr != NULL)
  203.             k->entry = ptr;
  204.     }
  205.     return (1);
  206. }
  207.  
  208. int
  209. getkey()
  210. {
  211.     key_entry_t *k;
  212.     int submatch;
  213.     static char buffer[128];
  214.     static char *record = buffer;
  215.  
  216.     /* If recorded bytes remain, return next recorded byte. */
  217.     if (*record != '\0')
  218.         return (*record++);
  219.     /* Reset record buffer. */
  220.     record = buffer;
  221.     do {
  222.         /* Read and record one byte. */
  223.         *record++ = getch();
  224.         *record = '\0';
  225.  
  226.         /* If recorded bytes match any multi-byte sequence... */
  227.         for (k = key_table, submatch = 0; k->entry != NULL; ++k) {
  228.             char *p, *q; 
  229.             for (p = buffer, q = k->entry; *p == *q; ++p, ++q) {
  230.                 if (*p == '\0') {
  231.                     /* Return extended key code. */
  232.                     return (k->code);
  233.                 }
  234.             }
  235.             if (*p == '\0') {
  236.                 /* Recorded bytes match anchored substring. */
  237.                 submatch = 1;
  238.             }
  239.         }
  240.         /* If recorded bytes matched an anchored substring, loop. */
  241.     } while (submatch);
  242.     /* Return first recorded byte. */
  243.     record = buffer;
  244.     return (*record++);
  245. }
  246.  
  247. #else /* not TERMCAP */
  248.  
  249. #define initkey()    keypad(stdscr,1)
  250. #define getkey()    getch()
  251.  
  252. #endif /* TERMCAP */
  253.  
  254. /* ASCII Control Codes */
  255. #undef CTRL
  256. #define CTRL(x)        ((x) & 0x1f)
  257. #define DEL        0x7f
  258.  
  259. char help_ea[] = "\
  260. Left, right, up, down\tarrow keys\tBeginning and end of line\t^A ^D\n\
  261. Word left and right\t^W ^E\t\tTop and bottom of file\t\t^T ^B\n\
  262. Page up and down\t^P ^N\t\tDelete left and right\tbackspace DEL\n\
  263. Insert\t\t\ttyped keys\tHelp on and off\t\t\tF1\n\
  264. Save file\t\t^F\t\tRedraw\t\t\t\t^R\n\
  265. Quit\t\t\t^C\t\tFlip to VI-style\t\t^Z\n\
  266. ....5...10....5...20....5...30....5...40....5...50....5...60....5...70....5...80";
  267.  
  268. keytable_t modeless[] = {
  269.     { KEY_LEFT, left },
  270.     { KEY_RIGHT, right },
  271.     { KEY_DOWN, down },
  272.     { KEY_UP, up },
  273.     { CTRL('w'), wleft },
  274.     { CTRL('e'), wright },
  275.     { CTRL('n'), pgdown },
  276.     { CTRL('p'), pgup },
  277.     { CTRL('a'), lnbegin },
  278.     { CTRL('d'), lnend },
  279.     { CTRL('t'), top },
  280.     { CTRL('b'), bottom },
  281.     { KEY_BACKSPACE, backsp },
  282.     { '\b', backsp },
  283.     { KEY_DC, delete },
  284.     { DEL, delete },
  285.     { CTRL('f'), file },
  286.     { CTRL('r'), redraw },
  287.     { CTRL('c'), quit },
  288.     { CTRL('z'), flip },
  289.     { KEY_F(1), help },
  290.     { 0, insert }
  291. };
  292.  
  293. char help_ae[] = "\
  294. Left, right, up, down\th  j  k  l    \tBeginning and end of line\t[  ]\n\
  295. Word left and right\tH  L\t\tTop and bottom of file\t\tt  b\n\
  296. Page up and down\tJ  K\t\tDelete left and right\t\tX  x\n\
  297. Insert on and off\ti  ^L\t\tHelp on and off\t\t\t? \n\
  298. Save file\t\tF\t\tRedraw\t\t\t\tR\n\
  299. Quit\t\t\tQ\t\tFlip to EMACS-style\t\tZ\n\
  300. ....5...10....5...20....5...30....5...40....5...50....5...60....5...70....5...80";
  301.  
  302. keytable_t modual[] = {
  303.     { 'h', left },
  304.     { 'j', down },
  305.     { 'k', up },
  306.     { 'l', right },
  307.     { 'H', wleft },
  308.     { 'J', pgdown },
  309.     { 'K', pgup },
  310.     { 'L', wright },
  311.     { '[', lnbegin },
  312.     { ']', lnend },
  313.     { 't', top },
  314.     { 'b', bottom },
  315.     { 'i', insert_mode },
  316.     { 'x', delete },
  317.     { 'X', backsp },
  318.     { 'F', file },
  319.     { 'R', redraw },
  320.     { 'Q', quit },
  321.     { 'Z', flip },
  322.     { '?', help },
  323.     { 0, movegap }
  324. };
  325.  
  326. #ifdef POSIX
  327.  
  328. #include <termios.h>
  329.  
  330. /*
  331.  *    Set the desired input mode.
  332.  *
  333.  *    FALSE enables immediate character processing (disable signals 
  334.  *    and line processing.)  TRUE enables line processing and signals
  335.  *    (disables immediate character processing).  In either case flow 
  336.  *    control (XON/XOFF) is still active.  
  337.  *
  338.  *    If the termios function calls fail, then fall back on using 
  339.  *    CURSES' raw()/noraw() functions; however flow control will be 
  340.  *    affected.
  341.  */
  342. void
  343. lineinput(bf)
  344. int bf;
  345. {
  346.     int error;
  347.     struct termios term;
  348.     error = tcgetattr(fileno(stdin), &term) < 0;
  349.     if (!error) {
  350.         if (bf)
  351.             term.c_lflag |= ISIG | ICANON;
  352.         else
  353.             term.c_lflag &= ~(ISIG | ICANON);
  354.         error = tcsetattr(fileno(stdin), TCSANOW, &term) < 0;
  355.     }
  356.     /* Fall back on CURSES functions that do almost what we need if
  357.      * either tcgetattr() or tcsetattr() fail.
  358.      */
  359.     if (error) {
  360.         if (bf)
  361.             noraw();
  362.         else
  363.             raw();
  364.     }
  365. }
  366.  
  367. #else /* not POSIX */
  368.  
  369. #define lineinput(bf)    (bf ? noraw() : raw());
  370.  
  371. #endif /* POSIX */
  372.  
  373.  
  374. char *
  375. ptr(offset)
  376. int offset;
  377. {
  378.     if (offset < 0)
  379.         return (buf);
  380.     return (buf+offset + (buf+offset < gap ? 0 : egap-gap));
  381. }
  382.  
  383. int
  384. pos(pointer)
  385. char *pointer;
  386. {
  387.     return (pointer-buf - (pointer < egap ? 0 : egap-gap)); 
  388. }
  389.  
  390. void
  391. top()
  392. {
  393.     point = 0;
  394. }
  395.  
  396. void
  397. bottom()
  398. {
  399.     epage = point = pos(ebuf);
  400. }
  401.  
  402. void
  403. quit()
  404. {
  405.     done = 1;
  406. }
  407.  
  408. void
  409. redraw()
  410. {
  411.     clear();
  412.     if (helpline == HELP_LINE)
  413.         mvaddstr(0, 0, table == modual ? help_ae : help_ea);
  414.     display();
  415. }
  416.  
  417. void
  418. movegap()
  419. {
  420.     char *p = ptr(point);
  421.     while (p < gap)
  422.         *--egap = *--gap;
  423.     while (egap < p)
  424.         *gap++ = *egap++;
  425.     point = pos(egap);
  426. }
  427.  
  428. int
  429. prevline(offset)
  430. int offset;
  431. {
  432.     char *p;
  433.     while (buf < (p = ptr(--offset)) && *p != '\n')
  434.         ;
  435.     return (buf < p ? ++offset : 0);
  436. }
  437.  
  438. int
  439. nextline(offset)
  440. int offset;
  441. {
  442.     char *p;
  443.     while ((p = ptr(offset++)) < ebuf && *p != '\n')    
  444.         ;
  445.     return (p < ebuf ? offset : pos(ebuf));
  446. }
  447.  
  448. int
  449. adjust(offset, column)
  450. int offset, column;
  451. {
  452.     char *p;
  453.     int i = 0;
  454.     while ((p = ptr(offset)) < ebuf && *p != '\n' && i < column) {
  455.         i += *p == '\t' ? 8-(i&7) : 1;
  456.         ++offset;
  457.     }
  458.     return (offset);
  459. }
  460.  
  461. void
  462. left()
  463. {
  464.     if (0 < point)
  465.         --point;
  466.  
  467. void
  468. right()
  469. {
  470.     if (point < pos(ebuf))
  471.         ++point;
  472. }
  473.  
  474. void
  475. up()
  476. {
  477.     point = adjust(prevline(prevline(point)-1), col);
  478. }
  479.  
  480. void
  481. down()
  482. {
  483.     point = adjust(nextline(point), col);
  484. }
  485.  
  486. void
  487. lnbegin()
  488. {
  489.     point = prevline(point);
  490. }
  491.  
  492. void
  493. lnend()
  494. {
  495.     point = nextline(point);
  496.     left();
  497. }
  498.  
  499. void
  500. wleft()
  501. {
  502.     char *p;
  503.     while (!isspace(*(p = ptr(point))) && buf < p)
  504.         --point;
  505.     while (isspace(*(p = ptr(point))) && buf < p)
  506.         --point;
  507. }
  508.  
  509. void
  510. pgdown()
  511. {
  512.     page = point = prevline(epage-1);
  513.     while (0 < row--)
  514.         down();
  515.     epage = pos(ebuf);
  516. }
  517.  
  518. void
  519. pgup()
  520. {
  521.     int i = LINES;
  522.     while (0 < --i) {
  523.         page = prevline(page-1); 
  524.         up();
  525.     }
  526. }
  527.  
  528. void
  529. wright()
  530. {
  531.     char *p;
  532.     while (!isspace(*(p = ptr(point))) && p < ebuf)
  533.         ++point;
  534.     while (isspace(*(p = ptr(point))) && p < ebuf)
  535.         ++point;
  536. }
  537.  
  538. void
  539. insert()
  540. {
  541.     movegap();
  542.     if (gap < egap)
  543.         *gap++ = input == '\r' ? '\n' : input;
  544.     point = pos(egap);
  545. }
  546.  
  547. void
  548. insert_mode()
  549. {
  550.     int ch;
  551.     movegap();
  552.     while ((ch = getkey()) != '\f') {
  553.         if (ch == '\b') {
  554.             if (buf < gap)
  555.                 --gap;
  556.         } else if (gap < egap) {
  557.             *gap++ = ch == '\r' ? '\n' : ch;
  558.         }
  559.         point = pos(egap);
  560.         display();
  561.     }
  562. }
  563.  
  564. void
  565. backsp()
  566. {
  567.     movegap();
  568.     if (buf < gap)
  569.         --gap;
  570.     point = pos(egap);
  571. }
  572.  
  573. void
  574. delete()
  575. {
  576.     movegap();
  577.     if (egap < ebuf)
  578.         point = pos(++egap);
  579. }
  580.  
  581. void
  582. file()
  583. {
  584.     if (!save(filename))
  585.         save(HUP);
  586. }
  587.  
  588. int
  589. save(fn)
  590. char *fn;
  591. {
  592.     FILE *fp;
  593.     int i, ok;
  594.     size_t length;
  595.     fp = fopen(fn, "w");
  596.     if ((ok = fp != NULL)) {
  597.         i = point;
  598.         point = 0;
  599.         movegap();
  600.         length = (size_t) (ebuf-egap);
  601.         ok = fwrite(egap, sizeof (char), length, fp) == length;
  602.         (void) fclose(fp);
  603.         point = i;
  604.     }
  605.     return (ok);
  606. }
  607.  
  608. void
  609. flip()
  610. {
  611.     table = table == modual ? modeless : modual;
  612.     if (helpline == HELP_LINE) 
  613.         mvaddstr(0, 0, table == modual ? help_ae: help_ea);
  614. }
  615.  
  616. void
  617. help()
  618. {
  619.     helpline = helpline == 0 ? HELP_LINE : 0;
  620.     redraw();
  621. }
  622.     
  623. void
  624. display()
  625. {
  626.     char *p;
  627.     int i, j;
  628.     if (point < page)
  629.         page = prevline(point);
  630.     if (epage <= point) {
  631.         page = nextline(point); 
  632.         i = (page == pos(ebuf) ? LINES-2 : LINES) - helpline; 
  633.         while (0 < i--)
  634.             page = prevline(page-1);
  635.     }
  636.     move(helpline, 0);
  637.     i = helpline;
  638.     j = 0;
  639.     epage = page;
  640.     while (1) {
  641.         if (point == epage) {
  642.             row = i;
  643.             col = j;
  644.         }
  645.         p = ptr(epage);
  646.         if (LINES <= i || ebuf <= p)
  647.             break;
  648.         if (*p != '\r') {
  649.             addch(*p);
  650.             j += *p == '\t' ? 8-(j&7) : 1;
  651.         }
  652.         if (*p == '\n' || COLS <= j) {
  653.             ++i;
  654.             j = 0;
  655.         }
  656.         ++epage;
  657.     }
  658.     clrtobot();
  659.     if (++i < LINES)
  660.         mvaddstr(i, 0, "<< EOF >>");
  661.     move(row, col);
  662.     refresh();
  663. }
  664.  
  665. int
  666. main(argc, argv)
  667. int argc;
  668. char **argv;
  669. {
  670.     FILE *fp;
  671.     char *p = *argv;
  672.     int i = (int) strlen(p);
  673.     egap = ebuf = buf + BUF;
  674.     if (argc < 2)
  675.         return (2);
  676.     /* Find basename. */
  677.     while (0 <= i && p[i] != '\\' && p[i] != '/')
  678.         --i;
  679.     p += i+1;
  680.     if (strncmp(p, "ae", 2) == 0 || strncmp(p, "AE", 2) == 0)
  681.         table = modual;
  682.     else if (strncmp(p, "ea", 2) == 0 || strncmp(p, "EA", 2) == 0)
  683.         table = modeless;
  684.     else
  685.         return (2);
  686.     if (initscr() == NULL)
  687.         return (3);
  688.     noecho();
  689.     lineinput(FALSE);
  690.     idlok(stdscr, TRUE);
  691.     initkey();
  692.     fp = fopen(filename = *++argv, "r");
  693.     if (fp != NULL) {
  694.         gap += fread(buf, sizeof (char), (size_t) BUF, fp);
  695.         fclose(fp);
  696.     }
  697.     top();
  698.     help();
  699.     while (!done) {
  700.         display();
  701.         i = 0; 
  702.         input = getkey(); 
  703.         while (table[i].key != 0 && input != table[i].key)
  704.             ++i;
  705.         (*table[i].func)();
  706.     }
  707.     endwin();
  708.     return (0);
  709. }
  710.